﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace Kelper.Tests
{
    public class TestRoute
    {
        public static string Outgoing(string action, string controller, object properties = null)
        {
            RouteCollection routes = new RouteCollection();
            MvcApplication.RegisterRoutes(routes);
            RequestContext context = new RequestContext(CreateHttpContext(), new RouteData());
            RouteValueDictionary routeValues = properties == null ? null : new RouteValueDictionary(properties);

            return UrlHelper.GenerateUrl(null, action, controller, routeValues, routes, context, true);
        }

        public static bool Get(string url, string expectedController, string expectedAction,
            object routeProperties = null)
        {
            return TestRouteMatch(url, expectedController, expectedAction, routeProperties, "GET");
        }

        public static bool Post(string url, string expectedController, string expectedAction,
            object routeProperties = null)
        {
            return TestRouteMatch(url, expectedController, expectedAction, routeProperties, "POST");
        }

        private static bool TestRouteMatch(string url, string controller, string action,
            object routeProperties = null, string httpMethod = "GET")
        {
            if (!url.StartsWith("~"))
                url = "~" + url;

            RouteCollection routes = new RouteCollection();
            MvcApplication.RegisterRoutes(routes);

            RouteData result = routes.GetRouteData(CreateHttpContext(url, httpMethod));

            return result != null && TestIncomingRouteResult(result, controller, action, routeProperties);
        }

        private static bool TestIncomingRouteResult(RouteData routeResult, string controller,
            string action, object propertySet = null)
        {
            if (routeResult.Route == null)
                return false;

            Func<object, object, bool> valCompare = (v1, v2) =>
                StringComparer.InvariantCultureIgnoreCase.Compare(v1.ToString(), v2.ToString()) == 0;

            bool result = valCompare(routeResult.Values["controller"], controller)
                && valCompare(routeResult.Values["action"], action);

            if (propertySet != null)
            {
                PropertyInfo[] propInfo = propertySet.GetType().GetProperties();
                foreach (PropertyInfo pi in propInfo)
                {
                    if (!(routeResult.Values.ContainsKey(pi.Name)
                        && valCompare(routeResult.Values[pi.Name],
                            pi.GetValue(propertySet, null))))
                    {
                        result = false;
                        break;
                    }
                }
            }
            return result;
        }

        private static HttpContextBase CreateHttpContext(
            string targetUrl = "~/", string httpMethod = "GET")
        {
            // Create mock request
            Mock<HttpRequestBase> mockRequest = new Mock<HttpRequestBase>();
            mockRequest.Setup(m => m.AppRelativeCurrentExecutionFilePath).Returns(targetUrl);
            mockRequest.Setup(m => m.HttpMethod).Returns(httpMethod);

            // Create mock response
            Mock<HttpResponseBase> mockResponse = new Mock<HttpResponseBase>();
            mockResponse.Setup(m => m.ApplyAppPathModifier(
                It.IsAny<string>())).Returns<string>(s => s);

            // Crate mock context
            Mock<HttpContextBase> mockContext = new Mock<HttpContextBase>();
            mockContext.Setup(m => m.Request).Returns(mockRequest.Object);
            mockContext.Setup(m => m.Response).Returns(mockResponse.Object);

            return mockContext.Object;
        }
    }
}
